from utils.key import is_valid_redis_key, get_all_keys
# 导入所有的redis数据库资源,RS是RedisServer类的实例对象，RS.rDB[i]是获取到对应的数据库
from redisData.rServer import RS
import random

# SADD key member [member ...]
def sadd(cmd_list, rClient):
    # rclient.redisDbID # 是获取该client 使用的数据库id
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # 移除函数名

    if len(cmd_list) < 2:  # 如果没有传参数
        return "(error) ERR wrong number of arguments for 'sadd' command"
    key = cmd_list.pop(0)  # 获取键
    if not is_valid_redis_key(key):  # 判断键是否合法
        return "(error) Invalid key"
    
    keys_dict = get_all_keys(rClient)  # 获取已经存在的所有键
    if key in keys_dict:
        # 如果键存在且但是键类型不为set
         if keys_dict[key] != 'set':
            return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"
    # 键不存在，新添加一个
    else:
        # keys_dict[key] = 'set'
        set_data[key] =set()

    # 执行 SADD 操作
    added_count = 0
    for member in cmd_list:
        if member not in set_data[key]:
            set_data[key].add(member)
            added_count += 1

    return "(integer) {}".format(added_count)

# srem key member [member ...]
def srem(cmd_list, rClient):
    # rclient.redisDbID # is to get the database id used by this client
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # remove the function name

    if len(cmd_list) < 2:  # If no parameters are passed
        return "(error) ERR wrong number of arguments for 'srem' command"
    key = cmd_list.pop(0)  # Get the key
    if not is_valid_redis_key(key):  # Check if the key is valid
        return "(error) Invalid key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys
    if key in keys_dict:
        # If the key exists and its type is not set
        if keys_dict[key] != 'set':
            return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"
    # If the key does not exist, return 0
    else:
        return "(integer) 0"

    # Execute the SREM operation
    removed_count = 0
    for member in cmd_list:
        if member in set_data[key]:
            set_data[key].remove(member)
            removed_count += 1

    return "(integer) {}".format(removed_count)


# smembers key 
def smembers(cmd_list, rClient):
    # rclient.redisDbID # is to get the database id used by this client
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # remove the function name

    if len(cmd_list) != 1:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'smembers' command"
    key = cmd_list.pop(0)  # Get the key
    if not is_valid_redis_key(key):  # Check if the key is valid
        return "(error) Invalid key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys
    if key not in keys_dict or keys_dict[key] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    # If the key exists and its type is set, return all members
    members = set_data[key]
    result = "\n".join(["\"{}\"".format(member) for member in members])
    return result

# sismember key member
def sismember(cmd_list, rClient):
    # rclient.redisDbID # is to get the database id used by this client
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # remove the function name

    if len(cmd_list) != 2:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'sismember' command"

    key = cmd_list.pop(0)  # Get the key
    member = cmd_list.pop(0)  # Get the member

    if not is_valid_redis_key(key):  # Check if the key is valid
        return "(error) Invalid key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys
    if key not in keys_dict or keys_dict[key] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    # Check if the member is in the set
    result = 1 if member in set_data[key] else 0
    return "(integer) {}".format(result)


# spop key [count]
def spop(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # 移除函数名

    if len(cmd_list) > 2:  # 如果传入的参数数量不正确
        return "(error) ERR wrong number of arguments for 'spop' command"

    key = cmd_list.pop(0)  # 获取键
    if not is_valid_redis_key(key):  # 判断键是否合法
        return "(error) Invalid key"

    keys_dict = get_all_keys(rClient)  # 获取已经存在的所有键
    if key not in keys_dict or keys_dict[key] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    count = int(cmd_list[0]) if cmd_list else 1  # 获取 count 参数，默认为 1

    # 执行 SPOP 操作
    popped_elements = []
    for _ in range(min(count, len(set_data[key]))):
        popped_element = set_data[key].pop()
        popped_elements.append("\"{}\"".format(popped_element))

    # 如果集合为空，移除该键
    if not set_data[key]:
        del set_data[key]

    result = "\n".join(popped_elements)
    return result


# srandmember key [count]
def srandmember(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # remove the function name

    if len(cmd_list) > 2:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'srandmember' command"

    key = cmd_list.pop(0)  # Get the key
    if not is_valid_redis_key(key):  # Check if the key is valid
        return "(error) Invalid key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys
    if key not in keys_dict or keys_dict[key] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    count = int(cmd_list[0]) if cmd_list else 1  # Get count parameter, default is 1

    # Execute SRANDMEMBER operation
    members = set_data[key]

    if count > 0:
        # Return distinct elements as a formatted string
        result = "\n".join(["\"{}\"".format(member) for member in list(members)[:min(count, len(members))]])
        return result
    else:
        # Return with possible duplicates as a formatted string
        result = "\n".join(["\"{}\"".format(random.choice(list(members))) for _ in range(abs(count))])
        return result

# smove source destination member
def smove(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # Remove the function name

    if len(cmd_list) != 3:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'smove' command"

    source = cmd_list.pop(0)  # Get the source set
    destination = cmd_list.pop(0)  # Get the destination set
    member = cmd_list.pop(0)  # Get the member to move

    # Check if source, destination, and member are valid keys
    if not is_valid_redis_key(source) or not is_valid_redis_key(destination) or not is_valid_redis_key(member):
        return "(error) Invalid key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys

    # Check if source and destination are sets
    if source not in keys_dict or keys_dict[source] != 'set' or destination not in keys_dict or keys_dict[destination] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    # Check if the member is present in the source set
    if member not in set_data[source]:
        return "(integer) 0"  # Member not present in source set, no operation performed

    # Move the member from the source set to the destination set
    set_data[source].remove(member)
    set_data[destination].add(member)

    return "(integer) 1"


# scard key
def scard(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # Remove the function name

    if len(cmd_list) != 1:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'scard' command"

    key = cmd_list.pop(0)  # Get the key

    # Check if the key is a valid Redis key
    if not is_valid_redis_key(key):
        return "(error) Invalid key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys

    # Check if the key exists and is a set
    if key not in keys_dict or keys_dict[key] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    # Return the cardinality (number of elements) of the set
    return "(integer) {}".format(len(set_data[key]))



#sinter key [key...]
def sinter(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # Remove the function name

    if len(cmd_list) < 1:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'sinter' command"

    result_set = None

    for key in cmd_list:
        # Check if the key is a valid Redis key
        if not is_valid_redis_key(key):
            return "(error) Invalid key"

        keys_dict = get_all_keys(rClient)  # Get all existing keys

        # If the key exists and is a set, get the members
        if key in keys_dict and keys_dict[key] == 'set':
            current_set = set_data[key]

            # If it's the first set, assign it to the result_set
            if result_set is None:
                result_set = current_set
            else:
                # Find the intersection with the current set
                result_set = result_set.intersection(current_set)

    if result_set is not None:
        # Return the members of the intersection set
        result = "\n".join(["\"{}\"".format(member) for member in result_set])
        return result
    else:
        # If no sets were provided, return an empty set
        return "(empty set)"


# sinterstore destination key [key...]
def sinterstore(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # Remove the function name

    if len(cmd_list) < 2:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'sinterstore' command"

    destination_key = cmd_list.pop(0)  # Get the destination key
    if not is_valid_redis_key(destination_key):  # Check if the key is valid
        return "(error) Invalid destination key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys
    destination_exists = destination_key in keys_dict and keys_dict[destination_key] == 'set'

    # If destination key exists and is not a set, return an error
    if destination_exists and keys_dict[destination_key] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    result_set = None

    for key in cmd_list:
        # Check if the key is a valid Redis key
        if not is_valid_redis_key(key):
            return "(error) Invalid key"

        # If the key exists and is a set, get the members
        if key in keys_dict and keys_dict[key] == 'set':
            current_set = set_data[key]

            # If it's the first set, assign it to the result_set
            if result_set is None:
                result_set = current_set
            else:
                # Find the intersection with the current set
                result_set = result_set.intersection(current_set)

    if result_set is not None:
        # Store the result_set in the destination key
        set_data[destination_key] = result_set

        # Return the cardinality of the resulting set
        return "(integer) {}".format(len(result_set))
    else:
        # If no sets were provided, return 0
        return "(integer) 0"



# sunion key [key...]
def sunion(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # Remove the function name

    if len(cmd_list) < 1:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'sunion' command"

    result_set = set()

    for key in cmd_list:
        # Check if the key is a valid Redis key
        if not is_valid_redis_key(key):
            return "(error) Invalid key"

        keys_dict = get_all_keys(rClient)  # Get all existing keys

        # If the key exists and is a set, get the members
        if key in keys_dict and keys_dict[key] == 'set':
            current_set = set_data[key]

            # Perform union with the current set
            result_set = result_set.union(current_set)

    # Return the members of the union set
    result = "\n".join(["\"{}\"".format(member) for member in result_set])
    return result


# sunionstore destination key [key ...]
def sunionstore(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # Remove the function name

    if len(cmd_list) < 2:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'sunionstore' command"

    destination_key = cmd_list.pop(0)  # Get the destination key
    if not is_valid_redis_key(destination_key):  # Check if the key is valid
        return "(error) Invalid destination key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys
    destination_exists = destination_key in keys_dict and keys_dict[destination_key] == 'set'

    # If destination key exists and is not a set, return an error
    if destination_exists and keys_dict[destination_key] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    result_set = set()

    for key in cmd_list:
        # Check if the key is a valid Redis key
        if not is_valid_redis_key(key):
            return "(error) Invalid key"

        # If the key exists and is a set, get the members
        if key in keys_dict and keys_dict[key] == 'set':
            current_set = set_data[key]

            # Add the members of the current set to the result_set
            result_set.update(current_set)

    # Store the result_set in the destination key
    set_data[destination_key] = result_set

    # Return the cardinality of the resulting set
    return "(integer) {}".format(len(result_set))



# SDIFF key [key ..]
def sdiff(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # Remove the function name

    if len(cmd_list) < 2:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'sdiff' command"

    result_set = None

    for key in cmd_list:
        # Check if the key is a valid Redis key
        if not is_valid_redis_key(key):
            return "(error) Invalid key"

        keys_dict = get_all_keys(rClient)  # Get all existing keys

        # If the key exists and is a set, get the members
        if key in keys_dict and keys_dict[key] == 'set':
            current_set = set_data[key]

            # If it's the first set, assign it to the result_set
            if result_set is None:
                result_set = current_set
            else:
                # Find the difference with the current set
                result_set = result_set.difference(current_set)

    # Return the members of the difference set
    result = "\n".join(["\"{}\"".format(member) for member in result_set])
    return result


# sdiffstore destination key [key ...]
def sdiffstore(cmd_list, rClient):
    set_data = RS.rDB[rClient.redisDbId].set_data
    cmd_list.pop(0)  # Remove the function name

    if len(cmd_list) < 2:  # If incorrect number of parameters are passed
        return "(error) ERR wrong number of arguments for 'sdiffstore' command"

    destination_key = cmd_list.pop(0)  # Get the destination key
    if not is_valid_redis_key(destination_key):  # Check if the key is valid
        return "(error) Invalid destination key"

    keys_dict = get_all_keys(rClient)  # Get all existing keys
    destination_exists = destination_key in keys_dict and keys_dict[destination_key] == 'set'

    # If destination key exists and is not a set, return an error
    if destination_exists and keys_dict[destination_key] != 'set':
        return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"

    result_set = None

    for key in cmd_list:
        # Check if the key is a valid Redis key
        if not is_valid_redis_key(key):
            return "(error) Invalid key"

        # If the key exists and is a set, get the members
        if key in keys_dict and keys_dict[key] == 'set':
            current_set = set_data[key]

            # If it's the first set, assign it to the result_set
            if result_set is None:
                result_set = current_set
            else:
                # Find the difference with the current set
                result_set = result_set.difference(current_set)

    if result_set is not None:
        # Store the result_set in the destination key
        set_data[destination_key] = result_set

        # Return the cardinality of the resulting set
        return "(integer) {}".format(len(result_set))
    else:
        # If no sets were provided, return 0
        return "(integer) 0"

